package typeErasure;


import java.util.Random;

/**
 * 定义一个生产器实现这个接口，虽然只是创建了一个泛型接口Generator<T>
 * 但是可以为T传入无数个实参， 形成无数种类型的Generator的接口
 * 在实现类实现泛型接口时，入已将泛型类型传入实参类型，则所有使用反省的地方都要替换成传入的实参类型
 * 即 Generator<T>, public T next(); 中的T都要替换为String类型
 */
public class FruitGenerator2 implements Generator<String> {

    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};

    @Override
    public String next() {
        Random random = new Random();
        return fruits[random.nextInt(3)];
    }

    public void showKeyValue1(GenericType<Number> obj) {
        System.out.println("generic test, key value is: " + obj.field1);
    }

    // 此处的 ？是类型实参
    public void showKeyValue2(GenericType<?> obj) {
        System.out.println("generic test, key value is: " + obj.field1);
    }


    /**
     *
     * @param tClass 传入泛型的实参
     * @param <T> T的类型
     * @return T 返回值为T的类型
     * public与返回值中间的<T>非常重要, 可以理解为声明的此方法为泛型方法。
     * 只有声明了<T>的方法才是泛型方法，泛型类中的使用泛型发方法成员并不是泛型方法
     * <T>标明该方法将使用泛型类型T，此时才可以在方法中使用泛型类型T
     * 与泛型类的定义一样，此处T可以随便写为任意标识，常见的如 T E K V等形式参数用于表示泛型
     */
    public <T> T genericMethod(Class<T> tClass) throws InstantiationException, IllegalAccessException {
        T instance = tClass.newInstance();
        return instance;
    }


    public static void main(String[] args) {
        GenericType<Integer> gInteger = new GenericType<Integer>(1234);
        GenericType<Number> gNumber = new GenericType<Number>(567);

        new FruitGenerator2().showKeyValue1(gNumber);

        // 编译错误
        // GenericType<Integer>  不能被看做 GenericType<Number> 的子类。
        // 可以得出 同一种泛型可以对应多个版本（因为参数是不确定的）， 不同版本反省的实例不兼容的
        // new FruitGenerator2().showKeyValue1(gInteger);


        try {
            Object obj = new FruitGenerator2().genericMethod(Class.forName("java.lang.String"));
            System.out.println(obj);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

}
